package com.uuah.server.component.model;

import java.beans.ExceptionListener;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.exception.lifecycle.InitialisationException;
import com.uuah.server.api.IUuahContext;
import com.uuah.server.api.component.ILifecycleAdapterFactory;
import com.uuah.server.api.component.model.IEntryPointResolver;
import com.uuah.server.api.component.model.IEntryPointResolverSet;
import com.uuah.server.api.component.model.IModel;
import com.uuah.server.api.context.IUuahContextAware;
import com.uuah.server.component.LifecycleAdapterFactory;
import com.uuah.server.component.model.resolvers.DefaultEntryPointResolverSet;
import com.uuah.server.component.model.resolvers.EntryPointResolverSet;
import com.uuah.server.component.service.ServiceExceptionStrategy;
import com.uuah.server.context.notification.ServerNotification;
import com.uuah.server.exception.AbstractUuahServerException;

public abstract class AbstractModel implements IModel {
	protected transient Logger logger = LoggerFactory.getLogger(getClass());

	public static final String DEFAULT_MODEL_NAME = "main";
	private String name = DEFAULT_MODEL_NAME;
	private IEntryPointResolverSet entryPointResolverSet = null;
	private ILifecycleAdapterFactory lifecycleAdapterFactory = new LifecycleAdapterFactory();
	private AtomicBoolean initialised = new AtomicBoolean(false);
	private AtomicBoolean started = new AtomicBoolean(false);
	private ExceptionListener exceptionListener = new ServiceExceptionStrategy();

	protected IUuahContext uuahContext;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public IEntryPointResolverSet getEntryPointResolverSet() {
		if (null == entryPointResolverSet) {
			entryPointResolverSet = new DefaultEntryPointResolverSet();
		}
		return entryPointResolverSet;
	}

	public void setEntryPointResolverSet(
			IEntryPointResolverSet entryPointResolverSet) {
		this.entryPointResolverSet = entryPointResolverSet;
	}

	public void setEntryPointResolvers(Collection entryPointResolvers) {
		if (null == entryPointResolverSet) {
			entryPointResolverSet = new EntryPointResolverSet();
		}
		for (Iterator resolvers = entryPointResolvers.iterator(); resolvers
				.hasNext();) {
			entryPointResolverSet
					.addEntryPointResolver((IEntryPointResolver) resolvers
							.next());
		}
	}

	public ILifecycleAdapterFactory getLifecycleAdapterFactory() {
		return lifecycleAdapterFactory;
	}

	public void setLifecycleAdapterFactory(
			ILifecycleAdapterFactory lifecycleAdapterFactory) {
		this.lifecycleAdapterFactory = lifecycleAdapterFactory;
	}

	public void dispose() {

	}

	public void stop() throws AbstractUuahServerException {
		started.set(false);
	}

	public void start() throws AbstractUuahServerException {
		if (!initialised.get()) {
			throw new IllegalStateException("Not Initialised");
		}

		if (!started.get()) {
			started.set(true);
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Model already started");
			}
		}
	}

	public void initialise() throws InitialisationException {
		if (!initialised.get()) {
			initialised.set(true);
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Model already initialised");
			}
		}
	}

	public ExceptionListener getExceptionListener() {
		return exceptionListener;
	}

	public void setExceptionListener(ExceptionListener exceptionListener) {
		this.exceptionListener = exceptionListener;
	}

	void fireNotification(ServerNotification notification) {
		if (uuahContext != null) {
			uuahContext.fireNotification(notification);
		} else if (logger.isDebugEnabled()) {
			logger
					.debug("UuahContext is not yet available for firing notifications, ignoring event: "
							+ notification);
		}
	}

	public void setUuahContext(IUuahContext context) {
		this.uuahContext = context;
		if (exceptionListener instanceof IUuahContextAware) {
			((IUuahContextAware) exceptionListener).setUuahContext(uuahContext);
		}
	}

}
